package com.yunlongn.common.json.jackson;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.yunlongn.common.json.AbstractJsonParser;
import com.yunlongn.common.json.TypeReference;
import com.yunlongn.common.json.config.JsonConfig;
import com.yunlongn.common.json.gson.GsonParser;
import com.yunlongn.common.json.jackson.deserializer.DateDeserializer;
import com.yunlongn.common.json.jackson.deserializer.StringDeserializer;
import com.yunlongn.common.json.jackson.factory.AbstractJsonDeserializer;
import com.yunlongn.common.json.jackson.factory.AbstractJsonSerializer;
import com.yunlongn.common.json.jackson.factory.JacksonExclusionFactory;
import com.yunlongn.common.json.jackson.factory.JsonSerializerFactory;
import com.yunlongn.common.json.jackson.serializer.*;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * jackson
 * @author Yun
 * @since 20210902
 */
public class JacksonParser extends AbstractJsonParser {


    public JacksonParser(JsonConfig config) {
        super(config);
    }

    public JacksonParser() {
        super(new JsonConfig());
    }
    /**
     * 反序列化对象实体
     */
    private ObjectMapper objectMapper;
    /**
     * 存放不同序列化类型的对象单例
     */
    private Map<String, ObjectMapper> OBJECT_MAPPER_MAP = new HashMap<>();


    @Override
    protected void newJsonParser(JsonConfig jsonConfig) {
        ObjectMapper objectMapper = new ObjectMapper();
        // 忽略无法转换的对象
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        // 简单的序列化 Module
        SimpleModule customModule = new SimpleModule();
        JsonSerializerFactory.addDeserializers(customModule, this.getConfig());
        objectMapper.registerModule(customModule);
        this.objectMapper = objectMapper;
    }


    private ObjectMapper getObjectMapper(boolean serializeNulls, String datePattern, Map<Class<?>, AbstractJsonSerializer<?>> jsonSerializerMap) {
        StringBuilder key = new StringBuilder((datePattern == null ? "null" : datePattern) + "::" + serializeNulls);
        if (jsonSerializerMap != null) {
            jsonSerializerMap.forEach((className,value) ->{
                key.append("::").append(value.getClass().getSimpleName());
            });
        }
        return mapperCache(serializeNulls, key.toString(), jsonSerializerMap);
    }

    private ObjectMapper mapperCache(boolean serializeNulls, String key, Map<Class<?>, AbstractJsonSerializer<?>> jsonSerializerMap) {
        ObjectMapper objectMapper = OBJECT_MAPPER_MAP.get(key);
        if (objectMapper == null) {
            synchronized (this) {
                objectMapper = OBJECT_MAPPER_MAP.get(key);
                if (objectMapper == null) {
                    objectMapper = new ObjectMapper();
                    objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
                    if (!serializeNulls) {
                        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
                    }
                    SimpleModule customModule = new SimpleModule();
                    JsonSerializerFactory.addSerializers(customModule, this.getConfig());
                    if (jsonSerializerMap != null) {
                        jsonSerializerMap.forEach((key2,value) -> {
                            value.setConfig(this.getConfig());
                            customModule.addSerializer((Class)key2, value);
                        });
                    }
                    objectMapper.registerModule(customModule);
                    Map<String, ObjectMapper> newMap = new HashMap<>(OBJECT_MAPPER_MAP);
                    newMap.put(key, objectMapper);
                    OBJECT_MAPPER_MAP = newMap;
                }
            }
        }
        return objectMapper;
    }

    @Override
    public <T> String toJsonString(T obj, boolean serializeNulls, String datePattern) {
        try {
            ObjectMapper objectMapper = getObjectMapper(serializeNulls, datePattern, null);
            // 判断是当前Class是否需要使用排除策略。例如： 某类是否有 transient 字段，可以缓存起来
            JacksonExclusionFactory.addExclusionStrategy(obj.getClass(), objectMapper);
            return objectMapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public <T> String toJsonString(T obj, boolean serializeNulls, String datePattern, Map<Class<?>, AbstractJsonSerializer<?>> jsonSerializerMap) {
        try {
            ObjectMapper objectMapper = getObjectMapper(serializeNulls, datePattern, jsonSerializerMap);
            // 判断是当前Class是否需要使用排除策略。例如： 某类是否有 transient 字段，可以缓存起来
            JacksonExclusionFactory.addExclusionStrategy(obj.getClass(), objectMapper);
            return objectMapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public <T> T fromJson(String jsonStr, Class<T> tClass) {
        try {
            return objectMapper.readValue(jsonStr, tClass);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public <T> T fromJson(InputStream jsonIn, Class<T> tClass) {
        try {
            return objectMapper.readValue(jsonIn, tClass);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public <T> T fromJson(String jsonStr, Type type) {
        try {
            TypeFactory typeFactory = objectMapper.getTypeFactory();
            JavaType javaType = typeFactory.constructType(type);
            return objectMapper.readValue(jsonStr, javaType);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public <T> T fromJson(InputStream jsonIn, Type type) {
        try {
            TypeFactory typeFactory = objectMapper.getTypeFactory();
            JavaType javaType = typeFactory.constructType(type);
            return objectMapper.readValue(jsonIn, javaType);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public <T> List<T> fromJsonArray(String jsonStr, TypeReference<List<T>> typeReference) {
        try {
            return objectMapper.readValue(jsonStr, new com.fasterxml.jackson.core.type.TypeReference<List<T>>() {
                @Override
                public Type getType() {
                    return typeReference.getType();
                }
            });
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}
